package glmodel;

import java.nio.FloatBuffer;

/**
 * Define a 4x4 matrix, and provide functions to create common matrices for 3D
 * operations, such as rotate, scale and translate.
 */
public class GL_Matrix
{
	// vars hold a 4x4 matrix, defaults to identity matrix.
	public float	m00	= 1, m01 = 0, m02 = 0, m03 = 0;
	public float	m10	= 0, m11 = 1, m12 = 0, m13 = 0;
	public float	m20	= 0, m21 = 0, m22 = 1, m23 = 0;
	public float	m30	= 0, m31 = 0, m32 = 0, m33 = 1;

	/**
	 * Default to the identity matrix
	 */
	public GL_Matrix()
	{
	}

	/**
	 * Create a matrix with the three given axes
	 */
	@SuppressWarnings("ucd")
	public GL_Matrix(GL_Vector right, GL_Vector up, GL_Vector forward)
	{
		m00 = right.x;
		m10 = right.y;
		m20 = right.z;
		m01 = up.x;
		m11 = up.y;
		m21 = up.z;
		m02 = forward.x;
		m12 = forward.y;
		m22 = forward.z;
	}

	///////////////////////////////////////////////////////////
	// Factory Methods
	///////////////////////////////////////////////////////////

	/**
	 * create a Matrix shifted by the given amounts
	 */
	public static GL_Matrix translateMatrix(float dx, float dy, float dz)
	{
		GL_Matrix m = new GL_Matrix();
		m.m03 = dx;
		m.m13 = dy;
		m.m23 = dz;
		return m;
	}

	/**
	 * create a Matrix to change scale
	 */
	public static GL_Matrix scaleMatrix(float dx, float dy, float dz)
	{
		GL_Matrix m = new GL_Matrix();
		m.m00 = dx;
		m.m11 = dy;
		m.m22 = dz;
		return m;
	}

	/**
	 * create a Matrix to scale all axes equally
	 */
	public static GL_Matrix scaleMatrix(float d)
	{
		return GL_Matrix.scaleMatrix(d, d, d);
	}

	/**
	 * create a rotation matrix
	 */
	public static GL_Matrix rotateMatrix(float dx, float dy, float dz)
	{
		GL_Matrix out = new GL_Matrix();
		float SIN;
		float COS;

		if (dx != 0)
		{
			GL_Matrix m = new GL_Matrix();
			SIN = (float) Math.sin(dx);
			COS = (float) Math.cos(dx);
			m.m11 = COS;
			m.m12 = SIN;
			m.m21 = -SIN;
			m.m22 = COS;
			out.transform(m);
		}
		if (dy != 0)
		{
			GL_Matrix m = new GL_Matrix();
			SIN = (float) Math.sin(dy);
			COS = (float) Math.cos(dy);
			m.m00 = COS;
			m.m02 = SIN;
			m.m20 = -SIN;
			m.m22 = COS;
			out.transform(m);
		}
		if (dz != 0)
		{
			GL_Matrix m = new GL_Matrix();
			SIN = (float) Math.sin(dz);
			COS = (float) Math.cos(dz);
			m.m00 = COS;
			m.m01 = SIN;
			m.m10 = -SIN;
			m.m11 = COS;
			out.transform(m);
		}
		return out;
	}

	/**
	 * Transform the given vector using this matrix. Return the transformed
	 * vector (the original vector is not modified).
	 * 
	 * @param v
	 *            GL_Vector to be transformed
	 * @return the transformed GL_Vector
	 */
	public GL_Vector transform(GL_Vector v)
	{
		if (v != null)
		{
			float newx, newy, newz;
			newx = v.x * m00 + v.y * m01 + v.z * m02 + m03;
			newy = v.x * m10 + v.y * m11 + v.z * m12 + m13;
			newz = v.x * m20 + v.y * m21 + v.z * m22 + m23;
			return new GL_Vector(newx, newy, newz);
		}
		return null;
	}

	/**
	 * transforms this matrix by matrix n from left (this=n x this)
	 */
	public void transform(GL_Matrix n)
	{
		GL_Matrix m = this.getClone();

		m00 = n.m00 * m.m00 + n.m01 * m.m10 + n.m02 * m.m20;
		m01 = n.m00 * m.m01 + n.m01 * m.m11 + n.m02 * m.m21;
		m02 = n.m00 * m.m02 + n.m01 * m.m12 + n.m02 * m.m22;
		m03 = n.m00 * m.m03 + n.m01 * m.m13 + n.m02 * m.m23 + n.m03;
		m10 = n.m10 * m.m00 + n.m11 * m.m10 + n.m12 * m.m20;
		m11 = n.m10 * m.m01 + n.m11 * m.m11 + n.m12 * m.m21;
		m12 = n.m10 * m.m02 + n.m11 * m.m12 + n.m12 * m.m22;
		m13 = n.m10 * m.m03 + n.m11 * m.m13 + n.m12 * m.m23 + n.m13;
		m20 = n.m20 * m.m00 + n.m21 * m.m10 + n.m22 * m.m20;
		m21 = n.m20 * m.m01 + n.m21 * m.m11 + n.m22 * m.m21;
		m22 = n.m20 * m.m02 + n.m21 * m.m12 + n.m22 * m.m22;
		m23 = n.m20 * m.m03 + n.m21 * m.m13 + n.m22 * m.m23 + n.m23;
	}

	/**
	 * transforms this matrix by matrix n from right (this=this x n)
	 */
	public void preTransform(GL_Matrix n)
	{
		GL_Matrix m = this.getClone();

		m00 = m.m00 * n.m00 + m.m01 * n.m10 + m.m02 * n.m20;
		m01 = m.m00 * n.m01 + m.m01 * n.m11 + m.m02 * n.m21;
		m02 = m.m00 * n.m02 + m.m01 * n.m12 + m.m02 * n.m22;
		m03 = m.m00 * n.m03 + m.m01 * n.m13 + m.m02 * n.m23 + m.m03;
		m10 = m.m10 * n.m00 + m.m11 * n.m10 + m.m12 * n.m20;
		m11 = m.m10 * n.m01 + m.m11 * n.m11 + m.m12 * n.m21;
		m12 = m.m10 * n.m02 + m.m11 * n.m12 + m.m12 * n.m22;
		m13 = m.m10 * n.m03 + m.m11 * n.m13 + m.m12 * n.m23 + m.m13;
		m20 = m.m20 * n.m00 + m.m21 * n.m10 + m.m22 * n.m20;
		m21 = m.m20 * n.m01 + m.m21 * n.m11 + m.m22 * n.m21;
		m22 = m.m20 * n.m02 + m.m21 * n.m12 + m.m22 * n.m22;
		m23 = m.m20 * n.m03 + m.m21 * n.m13 + m.m22 * n.m23 + m.m23;
	}


	/**
	 * return a string representation of this matrix
	 */
	public String toString()
	{
		StringBuffer out = new StringBuffer("<Matrix: \r\n");
		out.append(m00 + "," + m01 + "," + m02 + "," + m03 + ",\r\n");
		out.append(m10 + "," + m11 + "," + m12 + "," + m13 + ",\r\n");
		out.append(m20 + "," + m21 + "," + m22 + "," + m23 + ",\r\n");
		out.append(m30 + "," + m31 + "," + m32 + "," + m33 + ">\r\n");
		return out.toString();
	}

	/**
	 * return a copy of this matrix
	 */
	public GL_Matrix getClone()
	{
		GL_Matrix m = new GL_Matrix();
		m.m00 = m00;
		m.m01 = m01;
		m.m02 = m02;
		m.m03 = m03;
		m.m10 = m10;
		m.m11 = m11;
		m.m12 = m12;
		m.m13 = m13;
		m.m20 = m20;
		m.m21 = m21;
		m.m22 = m22;
		m.m23 = m23;
		m.m30 = m30;
		m.m31 = m31;
		m.m32 = m32;
		m.m33 = m33;
		return m;
	}
}